package com.techie.mdm.appbar.showcase;

import jdesktop.appbar.PropertyChangeSource;
import jdesktop.appbar.PropertyChangeSourceSupport;
import jdesktop.appbar.win32.WAppBar;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.beans.PropertyChangeListener;

/**
 * Manages a window that is associated with an appbar.
 */
public class AppBarWindow implements PropertyChangeSource
{
    /**
     * The current window associated with the appbar.
     */
    static public final String WINDOW = "window";

    /**
     * Boolean property, {@code true} if the appbar has a window.
     */
    static public final String HASWINDOW = "hasWindow";

    /**
     * The bounds of the window, or {@code null} if the window is {@code null}.
     * @see #getWindowBounds()
     */
    static public final String WINDOWBOUNDS = "windowBounds";

    /**
     * The reserved area of the appbar. Is {@code null} if the appbar is not docked.
     */
    static public final String APPBARBOUNDS = "appBarBounds";

    /**
     * The client area of the window.
     */
    static public final String WINDOWCLIENT = "windowClient";

    /**
     * Controls whether the window is registered with the appbar.
     * When {@code true}, the window is registered, when {@code false} the window is not registered
     * when created.
     */
    static public final String REGISTER = "register";


    private final PropertyChangeSourceSupport pcs;

    private final WAppBar appBar;
    private final AppBarBean  appBarBean;

    /**
     * The window. May be null if not-created or already disposed of.
     * If not-null, the window may or may not be reigstered with the appbar.
     */
    private Window window;

    /**
     * When true, the window is registered after creation.
     */
    private boolean register;

    /**
     * The reserved area.
     */
    private Rectangle reservedArea = new Rectangle();

    // todo - draggable (main boxy area), resiable
    

    private ComponentListener componentListener = new ComponentAdapter() {
        @Override
        public void componentResized(ComponentEvent e)
        {
            windowBoundsChanged();
        }

        @Override
        public void componentMoved(ComponentEvent e)
        {
            windowBoundsChanged();
        }
    };

    public AppBarWindow(AppBarBean appBarBean)
    {
        this.pcs = new PropertyChangeSourceSupport(this, true);
        this.appBarBean = appBarBean;
        this.appBar = appBarBean.getAppBar();
    }

    public boolean getHasWindow()
    {
        return window!=null;
    }

    /**
     * Calls {@link #createWindow} or {@link #disposeWindow()}
     * @param hasWindow
     */
    public void setHasWindow(boolean hasWindow)
    {
        boolean oldValue = getHasWindow();
        if (hasWindow)
            createWindow();
        else
            disposeWindow();
        if (hasWindow!=oldValue)
            pcs.firePropertyChange(HASWINDOW, oldValue, hasWindow);
    }

    public Window getWindow()
    {
        return window;
    }

    protected void setWindow(Window w)
    {
        if (w!=window)
        {
            Window oldValue = window;
            disposeWindow();
            window = w;
            window.addComponentListener(componentListener);
            pcs.firePropertyChange(WINDOW, oldValue, w);
        }
    }

    /**
     * Disposes the window and sets the {@link #WINDOW window} property to {@code null}.
     * If the window is already disposed, this method silently returns.
     */
    protected void disposeWindow()
    {
        Window w = window;
        if (w!=null)
        {
            appBar.unregister();
            w.removeComponentListener(componentListener);
            w.dispose();
            setWindow(null);
        }
        window = null;
    }

    /**
     * Creates a new window if one not already created.
     */
    protected void createWindow()
    {
        if (window==null)
        {
            Window w = createWindowImpl();
            setWindow(w);
        }
    }

    protected Window createWindowImpl()
    {
        // todo - create window with view
        JFrame frame = new JFrame(appBarBean.getName());
        frame.setVisible(true);
        return frame;
    }

    public void addPropertyChangeListener(PropertyChangeListener listener)
    {
        pcs.addPropertyChangeListener(listener);
    }

    public void removePropertyChangeListener(PropertyChangeListener listener)
    {
        pcs.removePropertyChangeListener(listener);
    }

    public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener)
    {
        pcs.removePropertyChangeListener(propertyName, listener);
    }

    public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener)
    {
        pcs.addPropertyChangeListener(propertyName, listener);
    }
    

    public Rectangle getWindowBounds()
    {
        return window==null ? null : window.getBounds();
    }

    /**
     * Sets the bounds for the window.
     * @param r
     */
    public void setWindowBounds(Rectangle r)
    {
        if (window!=null)
            window.setBounds(r);
    }

    public Rectangle getAppBarBounds()
    {
        return appBar.getReservedArea(reservedArea);
    }

    protected void windowBoundsChanged()
    {
        pcs.firePropertyChange(WINDOWBOUNDS, null, getWindowBounds());
        pcs.firePropertyChange(WINDOWCLIENT, null, getWindowClientArea());
        pcs.firePropertyChange(APPBARBOUNDS, null, getAppBarBounds());
    }

    public Rectangle getWindowClientArea()
    {
        return appBar.getWindowClientArea();
    }

    public boolean isRegistered()
    {
        return window!=null && appBar.isRegistered();
    }

    public void setRegistered(boolean registered)
    {
        if (window!=null)
        {
            if (registered)
                appBar.register(window, true);
            else
                appBar.unregister();
        }
    }

    /**
     * @see #REGISTER
     * @return
     */
    public boolean isRegister()
    {
        return register;
    }

    /**
     * @see #REGISTER
     * @param register
     */
    public void setRegister(boolean register)
    {
        if (this.register!=register)
        {
            this.register = register;
            pcs.firePropertyChange(REGISTER, !register, register);
        }
    }
}
